import java.util.LinkedList;


public class Node {
	Node parent;
	LinkedList<Node> children = new LinkedList<Node>();
	State currentState ;
	String currentMovement ; // operator used 
	// this list includes the current movemnt
	LinkedList <String> movementsSoFar = new LinkedList<String>();
	int Level ;
	Block blocktoBeMoved;
	
	public Node(Node parent, LinkedList<Node> children,
			LinkedList<String> movementsSoFar, int level,
			String currentMovement, State currentState , Block blocktoBeMoved) {
		super();
		this.parent = parent;
		this.children = children;
		this.movementsSoFar = movementsSoFar;
		this.Level = level;
		this.currentMovement = currentMovement;
		this.currentState = currentState;
		this. blocktoBeMoved =blocktoBeMoved;
	}


	public Node getParent() {
		return parent;
	}


	public void setParent(Node parent) {
		this.parent = parent;
	}


	public LinkedList<Node> getChildren() {
		return children;
	}


	public void setChildren(LinkedList<Node> children) {
		this.children = children;
	}


	public LinkedList<String> getMovementsSoFar() {
		return movementsSoFar;
	}


	public void setMovementsSoFar(LinkedList<String> movementsSoFar) {
		this.movementsSoFar = movementsSoFar;
	}


	public int getLevel() {
		return Level;
	}


	public void setLevel(int level) {
		Level = level;
	}


	public String getCurrentMovement() {
		return currentMovement;
	}


	public void setCurrentMovement(String currentMovement) {
		this.currentMovement = currentMovement;
	}


	public Block getBlocktoBeMoved() {
		return blocktoBeMoved;
	}


	public void setBlocktoBeMoved(Block blocktoBeMoved) {
		this.blocktoBeMoved = blocktoBeMoved;
	}


	public State getCurrentState() {
		return currentState;
	}


	public void setCurrentState(State currentState) {
		this.currentState = currentState;
	}
	
	public boolean isGoal(Node exploredNode)
	{
		
		if(exploredNode.getCurrentState().getCurrentBoard().getBlock(2, 5)!=null && exploredNode.getCurrentState().getCurrentBoard().getBlock(2, 5).getBlockNumber()==0)
		{
			return true;
		}
		return false;
	}

	public LinkedList<Pair> getSpaces(State currentState) {
		Board currentBoard = currentState.getCurrentBoard();
		LinkedList<Pair> emptySpace = new LinkedList<Pair>();

		for (int row = 0; row < 6; row++) {
			for (int coloumn = 0; coloumn < 6; coloumn++) {
				if (currentBoard.getBlock(row, coloumn) == null) {
					emptySpace.add(new Pair(row, coloumn));
				}
			}
		}
		return emptySpace;
	}

	public LinkedList<Node> insertInList(Node toBeExpanded) {
		Pair emptySpace;

		LinkedList<Pair> currentSpaces = getSpaces(toBeExpanded.getCurrentState());

		while (!currentSpaces.isEmpty()) {
			emptySpace = currentSpaces.pop();
			
			if (checkIfCanMoveDown(toBeExpanded.getCurrentState(), emptySpace)) {
				//moveDown(toBeExpanded, emptySpace);
			}
			if (checkIfCanMoveUp(toBeExpanded.getCurrentState(), emptySpace)) {
				//moveUp(toBeExpanded, emptySpace);
			}

			if (checkIfCanMoveLeft(toBeExpanded.getCurrentState(), emptySpace)) {
				//moveLeft(toBeExpanded, emptySpace);
			}
			if (checkIfCanMoveRight(toBeExpanded.getCurrentState(), emptySpace)) {
				moveRight(toBeExpanded, emptySpace);
			}

		}
		
		return toBeExpanded.getChildren() ;

	}

	public Boolean checkIfCanMoveDown(State stateBeforeMovement, Pair emptySpace) {

		if (emptySpace.getX() - 1 > -1
				&& stateBeforeMovement.getCurrentBoard().getBlock(
						emptySpace.getX() - 1, emptySpace.getY()) != null
				&& stateBeforeMovement.getCurrentBoard().getBlock(
						emptySpace.getX() - 1, emptySpace.getY()).state == 'V') {

//			System.out
//					.println("empty Space @ "
//							+ emptySpace.getX()
//							+ ","
//							+ emptySpace.getY()
//							+ " & Block number "
//							+ stateBeforeMovement.getCurrentBoard().getBlock(
//									emptySpace.getX() - 1, emptySpace.getY()).blockNumber
//							+ " can move down");

			return true;

		}
		return false;
	}

	public Boolean checkIfCanMoveUp(State stateBeforeMovement, Pair emptySpace) {

		if (emptySpace.getX() + 1 < 6
				&& stateBeforeMovement.getCurrentBoard().getBlock(
						emptySpace.getX() + 1, emptySpace.getY()) != null
				&& stateBeforeMovement.getCurrentBoard().getBlock(
						emptySpace.getX() + 1, emptySpace.getY()).state == 'V') {

//			System.out
//					.println("empty Space @ "
//							+ emptySpace.getX()
//							+ ","
//							+ emptySpace.getY()
//							+ " &  Block number "
//							+ stateBeforeMovement.getCurrentBoard().getBlock(
//									emptySpace.getX() + 1, emptySpace.getY()).blockNumber
//							+ " can move Up");

			return true;
		}
		return false;
	}

	public Boolean checkIfCanMoveLeft(State stateBeforeMovement, Pair emptySpace) {
		if (emptySpace.getY() + 1 < 6
				&& stateBeforeMovement.getCurrentBoard().getBlock(
						emptySpace.getX(), emptySpace.getY() + 1) != null
				&& (stateBeforeMovement.getCurrentBoard().getBlock(
						emptySpace.getX(), emptySpace.getY() + 1).state == 'H' || stateBeforeMovement
						.getCurrentBoard().getBlock(emptySpace.getX(),
								emptySpace.getY() + 1).state == 'M')) {

//			System.out
//					.println("empty Space @ "
//							+ emptySpace.getX()
//							+ ","
//							+ emptySpace.getY()
//							+ " &  Block number "
//							+ stateBeforeMovement.getCurrentBoard().getBlock(
//									emptySpace.getX(), emptySpace.getY() + 1).blockNumber
//							+ " can move left");
			return true;
		}
		return false;
	}

	public Boolean checkIfCanMoveRight(State stateBeforeMovement,
			Pair emptySpace) {

		if (emptySpace.getY() - 1 > -1
				&& stateBeforeMovement.getCurrentBoard().getBlock(
						emptySpace.getX(), emptySpace.getY() - 1) != null
				&& (stateBeforeMovement.getCurrentBoard().getBlock(
						emptySpace.getX(), emptySpace.getY() - 1).state == 'H' || stateBeforeMovement
						.getCurrentBoard().getBlock(emptySpace.getX(),
								emptySpace.getY() - 1).state == 'M')) {
//			System.out
//					.println("empty Space @ "
//							+ emptySpace.getX()
//							+ ","
//							+ emptySpace.getY()
//							+ " &  Block number "
//							+ stateBeforeMovement.getCurrentBoard().getBlock(
//									emptySpace.getX(), emptySpace.getY() - 1).blockNumber
//							+ " can move right");
			return true;
		}
		return false;
	}

	public void moveUp(Node toBeExpanded, Pair emptySpace) {

		LinkedList<Node> children = new LinkedList<Node>();
		int emptyR = emptySpace.getX();
		int emptyC = emptySpace.getY();
		LinkedList<String> movementsSoFar = toBeExpanded.movementsSoFar;
		movementsSoFar.add("Up");
		 Board currentBoard = toBeExpanded.getCurrentState().getCurrentBoard().copyBoard(toBeExpanded.getCurrentState().getCurrentBoard());
		if (emptyR + 3 < 6
				&& currentBoard.getBlock(emptyR + 3, emptyC) != null
				&& currentBoard.getBlock(emptyR + 1, emptyC).blockNumber == currentBoard
						.getBlock(emptyR + 3, emptyC).blockNumber) {
			currentBoard.addBlock(emptyR, emptyC,
					currentBoard.getBlock(emptyR + 3, emptyC));
			currentBoard.removeBlock(emptyR + 3, emptyC);
		} else {
			currentBoard.addBlock(emptyR, emptyC,
					currentBoard.getBlock(emptyR + 2, emptyC));
			currentBoard.removeBlock(emptyR + 2, emptyC);
		}

		State currentState = new State(currentBoard);
		
		Node BlockMovingUp = new Node(toBeExpanded, children, movementsSoFar,
				toBeExpanded.getLevel() + 1, "Up", currentState,
				currentBoard.getBlock(emptyR, emptyC));
		toBeExpanded.children.addLast(BlockMovingUp);

	}

	public void moveDown(Node toBeExpanded, Pair emptySpace) {

		LinkedList<Node> children = new LinkedList<Node>();
		int emptyR = emptySpace.getX();
		int emptyC = emptySpace.getY();
		LinkedList<String> movementsSoFar = toBeExpanded.movementsSoFar;
		movementsSoFar.add("Down");
		 Board currentBoard = toBeExpanded.getCurrentState().getCurrentBoard().copyBoard(toBeExpanded.getCurrentState().getCurrentBoard());
		if (emptyR - 3 > -1
				&& currentBoard.getBlock(emptyR - 3, emptyC) != null
				&& currentBoard.getBlock(emptyR - 1, emptyC).blockNumber == currentBoard
						.getBlock(emptyR - 3, emptyC).blockNumber) {
			currentBoard.addBlock(emptyR, emptyC,
					currentBoard.getBlock(emptyR - 3, emptyC));
			currentBoard.removeBlock(emptyR - 3, emptyC);
		} else {
			currentBoard.addBlock(emptyR, emptyC,
					currentBoard.getBlock(emptyR - 2, emptyC));
			currentBoard.removeBlock(emptyR - 2, emptyC);
		}

		State currentState = new State(currentBoard);


		Node BlockMovingDown = new Node(toBeExpanded, children, movementsSoFar,
				toBeExpanded.getLevel() + 1, "Down", currentState,
				currentBoard.getBlock(emptyR, emptyC));

		toBeExpanded.children.addLast(BlockMovingDown);
	}

	public void moveRight(Node toBeExpanded, Pair emptySpace1) {
		
		int currentLevel= toBeExpanded.Level;
		LinkedList<Node> mychildren = new LinkedList<Node>();
		int emptyR = emptySpace1.getX();
		int emptyC = emptySpace1.getY();
		
		LinkedList<String> movSoFar = copyLinkedList(toBeExpanded);
		movSoFar.addLast("Right");
		
		// creation of a new Board 
		Board temp= toBeExpanded.getCurrentState().getCurrentBoard().copyBoard(toBeExpanded.getCurrentState().getCurrentBoard());
		State myCurrentState = new State(temp);
	
		// create a pair && a block that is going to move
		Pair newLocation = new Pair (emptyR, emptyC);
		LinkedList<Pair> tempList = new  LinkedList<Pair>();
		
		Block tempBlock =temp.getBlock(emptyR, emptyC - 2);
		tempList = copyLinkedListOfPairs(tempBlock);
		System.out.println(tempBlock.getBlockNumber()+" "+ tempList.get(0).getY()+"," +tempList.get(1).getY() );
		tempList.removeFirst();
		tempList.addLast(newLocation);
		//System.out.println(tempBlock.getBlockNumber()+" #"+ tempList.get(0).getY()+"," +tempList.get(1).getY() );
		
		Block toBeMoved = new Block(tempBlock.getState(),tempList,tempBlock.getBlockNumber());
		
		// removing the old block
		myCurrentState.getCurrentBoard().removeBlock(emptyR, emptyC - 2);
		myCurrentState.getCurrentBoard().removeBlock(emptyR, emptyC - 1);
		
		// adding the tile to the board
		myCurrentState.getCurrentBoard().addBlock(emptyR, emptyC,
				toBeMoved);
		
		myCurrentState.getCurrentBoard().addBlock(emptyR, emptyC-1,
				toBeMoved);

		Node BlockMovingRight = new Node(toBeExpanded, mychildren,
				movSoFar,currentLevel+1, "Right", myCurrentState,
				toBeMoved);
		
		toBeExpanded.getChildren().addLast(BlockMovingRight);


	}

	public void moveLeft(Node toBeExpanded, Pair emptySpace) {
		int currentLevel= toBeExpanded.Level;
		LinkedList<Node> mychildren = new LinkedList<Node>();
		int emptyR = emptySpace.getX();
		int emptyC = emptySpace.getY();
		
		LinkedList<String> movSoFar = copyLinkedList(toBeExpanded);
		movSoFar.add("Left");
		
		// creation of a new Board 
				Board temp= toBeExpanded.getCurrentState().getCurrentBoard().copyBoard(toBeExpanded.getCurrentState().getCurrentBoard());
				State myCurrentState = new State(temp);
	 
		// create a pair && a block that is going to move
			Pair newLocation = new Pair (emptyR, emptyC);
			LinkedList<Pair> tempList = new  LinkedList<Pair>();
			
			Block tempBlock =myCurrentState.getCurrentBoard().getBlock(emptyR, emptyC + 2);
			tempList = tempBlock.getLocation();
			tempList.removeLast();
			tempList.addFirst(newLocation);
			
			Block toBeMoved = new Block(tempBlock.getState(),tempList,tempBlock.getBlockNumber());
			
			// removing the old block
			myCurrentState.getCurrentBoard().removeBlock(emptyR, emptyC + 2);
			myCurrentState.getCurrentBoard().removeBlock(emptyR, emptyC + 1);
			
			// adding the tile to the board
			myCurrentState.getCurrentBoard().addBlock(emptyR, emptyC,toBeMoved);		
			myCurrentState.getCurrentBoard().addBlock(emptyR, emptyC+1,toBeMoved);
			
		Node BlockMovingLeft = new Node(toBeExpanded, mychildren, movSoFar,
				currentLevel+1, "Left", myCurrentState,
				toBeMoved);
		
		toBeExpanded.getChildren().addLast(BlockMovingLeft);


	}
	
	public LinkedList<String> addListToList(LinkedList<String>movementsSoFar ,LinkedList<String> PreviousMovement )
	{
		int size = PreviousMovement.size();
		for (int i=0; i< size ;i++)
		{
			movementsSoFar.addLast(PreviousMovement.pop());
		}
		return movementsSoFar;
		
	}

	public LinkedList<String> copyLinkedList(Node toBeExpanded)
	{
		LinkedList<String> beta3 =toBeExpanded.getMovementsSoFar();
		LinkedList<String> Rage3 = new LinkedList<String>();
		for(int i =0 ; i<beta3.size();i++)
		{
			Rage3.addLast(beta3.get(i));
		}
		return Rage3;
	}
	
	public LinkedList<Pair> copyLinkedListOfPairs(Block b)
	{
		LinkedList<Pair> beta3 =b.getLocation();
		LinkedList<Pair> Rage3 = new LinkedList<Pair>();
		for(int i =0 ; i<beta3.size();i++)
		{
			Rage3.addLast(beta3.get(i));
		}
		return Rage3;
	}
}
